#include "common.h"
#include "my_ioctl.h"
#include <linux/socket.h>
#include <linux/net.h>

#define PRINT_LEVEL KERN_INFO

#define MAX_SOCK_ADDR	128
static long my_bind(struct socket *sock, struct sockaddr *umyaddr, int addrlen)
{
	char address[MAX_SOCK_ADDR];
	int err;

    get_file(sock->file);
    err = move_addr_to_kernel(umyaddr, addrlen, address);
    if(err >= 0) {
        err = security_socket_bind(sock, (struct sockaddr *)address, addrlen);
        if (err) {
            fput(sock->file);
            return err;
        }
        err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen);
    }
    fput(sock->file);    
	return err;
}

static long my_socket(my_socket_arg *arg)
{
    int ret;
	struct socket *sock;

	ret = sock_create(AF_INET, SOCK_STREAM, 0, &sock);
	if (ret < 0)
		goto out;

	ret = sock_map_fd(sock);
	if (ret < 0)
		goto out_release;

    if (my_bind(sock, arg->addr, arg->addrlen) != 0)
        goto out_release;
out:
	/* It may be already another descriptor 8) Not kernel problem. */
	return ret;

out_release:
	sock_release(sock);
	return ret;
}

extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);

static int my_tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
    int ret;
    my_net_ioctl buf;
	struct request_sock *acc_req;
	struct request_sock *req;
	struct tcp_sock *tp = tcp_sk(sk);    
    my_socket_arg *skt_arg;

    if (cmd == MY_NET_IOCTL) {
        printk(PRINT_LEVEL  "%s : cmd = %x\n", __FUNCTION__, cmd);

        ret = copy_from_user(&buf, (void *)arg, sizeof(my_net_ioctl));
        if (ret != 0) {
            printk(PRINT_LEVEL  "copy from user fail\n");
            return (-EFAULT);
        }
        switch (buf.cmd)
        {
            case CMD_CLOSE:
                acc_req = tp->accept_queue.rskq_accept_head;
                while ((req = acc_req) != NULL) {
                    struct sock *child = req->sk;
                    int old_state = child->sk_state;
                    acc_req = req->dl_next;
                    if (old_state != TCP_CLOSE)
                        tcp_set_state(sk, TCP_CLOSE);
                }
                break;
            case CMD_SOCKET:
                skt_arg = ((my_net_ioctl *)arg)->cmd_arg;
                ret = my_socket(skt_arg);
                copy_to_user(&skt_arg->ret, &ret, sizeof(int));
                break;
            default:
                return (-ENOIOCTLCMD);
        }
        return (0);
    }
    
    memcpy(target_func, saved_st, 4 * NUM_NEED);
    clean_cache(target_func, target_func + NUM_NEED);

    ret = tcp_ioctl(sk, cmd, arg);
    
	hijack_func();
    return (ret);
}

void jmpfunc(void)
{
    __asm__ __volatile__ ("mov %%pc, %0"
                          ::"r"((void *)my_tcp_ioctl));    
}


int net_ioctl_init(void)
{
    target_func = tcp_ioctl;
    printk(PRINT_LEVEL  "%s init\n", __FUNCTION__);
    save_old(target_func);
	hijack_func();
    return 0;
}

void net_ioctl_exit(void)
{
    memcpy((void *)target_func, saved_st, 4 * NUM_NEED);
    clean_cache((void *)target_func, (void *)target_func + NUM_NEED);    
	printk(PRINT_LEVEL  "%s exit\n", __FUNCTION__);
}

module_init(net_ioctl_init);
module_exit(net_ioctl_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("tpl.a1406@gmail.com");


